package FrameWork.EventBus;

import FrameWork.EventBus.Anno.Schedule;
import FrameWork.Executor.EventLoop;
import FrameWork.Executor.EventLoopGroup;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

public class EventBusManager {

    private static Map<Class<? extends IEvent>,List<ReceiveHandler>> eventToHandlers = new HashMap<>();
    private static Map<Class<?>,Integer> actorDispatcherCodes = new HashMap<>();
    //事件总线线程
    private static EventLoopGroup eventLoopGroup = new EventLoopGroup();
    //worker线程
    private static EventLoopGroup workerGroup = new EventLoopGroup(20);
    private static final int loopSize = eventLoopGroup.getLoopAmount();

    static {
        //启动线程组，会阻塞到所有线程启动完毕
        eventLoopGroup.init();
        workerGroup.init();
    }

    public static void submit(int dispatcherCode,String name,IEvent event){
        dispatcherCode = hash(dispatcherCode);
        List<ReceiveHandler> handlers = eventToHandlers.get(event.getClass());
        if (handlers == null){
            return;
        }
        for (ReceiveHandler handler:handlers){
            ScheduleType scheduleType = handler.getScheduleType();
            Runnable task = wrapTask(handler,event);
            if (scheduleType == ScheduleType.INHERIT || scheduleType == ScheduleType.ASYNC){
                eventLoopGroup.submit(dispatcherCode,name,task);
            }
            else if (scheduleType == ScheduleType.ACTOR){
                int code = actorDispatcherCodes.get(handler.getBean().getClass());
                eventLoopGroup.submit(code,name,task);
            }
            else if (scheduleType == ScheduleType.SYNC){
                task.run();
            }
            else if (scheduleType == ScheduleType.WORKER){
                workerGroup.submit("name",task);
            }
        }
    }

    public static void syncSubmit(String name,IEvent event){
        List<ReceiveHandler> handlers = eventToHandlers.get(event.getClass());
        if (handlers == null){
            return;
        }
        for (ReceiveHandler handler:handlers){
            ScheduleType scheduleType = handler.getScheduleType();
            Runnable task = wrapTask(handler,event);
            if (scheduleType == ScheduleType.INHERIT || scheduleType == ScheduleType.SYNC){
                task.run();
            }
            else if (scheduleType == ScheduleType.ACTOR){
                int code = actorDispatcherCodes.get(handler.getBean().getClass());
                eventLoopGroup.submit(code,name,task);
            }
            else if (scheduleType == ScheduleType.ASYNC){
                eventLoopGroup.submit(name,task);
            }
            else if (scheduleType == ScheduleType.WORKER){
                workerGroup.submit("name",task);
            }
        }
    }

    public static void submitTask(int code,Runnable runnable){
        eventLoopGroup.submit(code,null,runnable);
    }

    public static void submitTask(ITask task){
        if (task.getDispatcherCode()<0){
            eventLoopGroup.submit(null,task);
        }else{
            eventLoopGroup.submit(task.getDispatcherCode(),null,task);
        }
    }

    public static ScheduledFuture submitScheduleTask(long period, Runnable runnable){
        return eventLoopGroup.setPeriod(period, runnable);
    }

    public static boolean inLoop(int code){
        code = hash(code);
        return eventLoopGroup.inLoop(code);
    }

    private static Runnable wrapTask(ReceiveHandler handler,IEvent event){
        return new Runnable() {
            @Override
            public void run() {
                handler.invoke(event);
            }
        };
    }

    private static int hash(int code){
        int h;
        return (code<0) ? 0 : ((h = code) ^ (h >>> 16)) % (loopSize);
    }

    static void registerHandler(Class<? extends IEvent>type, Method method,Object bean,ScheduleType scheduleType){
        //注册Actor
        if (scheduleType == ScheduleType.ACTOR && !actorDispatcherCodes.containsKey(bean.getClass())){
            registerActor(bean);
        }
        //实例化ReceiveHandler
        ReceiveHandler handler = new ReflectReceiveHandler(bean,method,scheduleType);
        if (!eventToHandlers.containsKey(type)){
            eventToHandlers.put(type,new LinkedList<>());
        }
        List<ReceiveHandler> list = eventToHandlers.get(type);
        int idx = 0;
        for (ReceiveHandler node:list){
            if (handlerComparator.compare(handler,node)<=0){
                break;
            }
            idx++;
        }
        list.add(idx,handler);
    }

    static void registerActor(Object bean){
        Class actorClazz = bean.getClass();
        if (!actorDispatcherCodes.containsKey(actorClazz)){
            int h;
            if(Emitter.class.isAssignableFrom(actorClazz)){
                Emitter emitter = (Emitter)bean;
                h = emitter.getDispatcherCode();
            }else{
                h = actorClazz.hashCode();
            }
            actorDispatcherCodes.put(actorClazz,hash(h));
        }
    }


    private static Map<ScheduleType,Integer> orders = new HashMap<>();
    static{
        orders.put(ScheduleType.ACTOR,1);
        orders.put(ScheduleType.WORKER,2);
        orders.put(ScheduleType.ASYNC,2);
        orders.put(ScheduleType.INHERIT,3);
        orders.put(ScheduleType.SYNC,4);
    }

    private static Comparator<ReceiveHandler> handlerComparator = new Comparator<ReceiveHandler>() {
        @Override
        public int compare(ReceiveHandler o1, ReceiveHandler o2) {
            ScheduleType type1 = o1.getScheduleType();
            ScheduleType type2 = o2.getScheduleType();
            return orders.get(type1) - orders.get(type2);
        }
    };



}
